(function () {
	return {
		create: function ($, canvas, fnGetDC, fnGetScale, fnDocSizeChange) {
			var wf = {};

			//常量
			wf.HANDLE_WIDTH = 7; //句柄大小
			wf.PROC_WIDTH = 128; //环节宽度
			wf.PROC_HEIGHT = 52; //环节高度
			wf.TOLERANCE_MOVE = 3; //允许的误差大小（鼠标拖动时）
			wf.TOLERANCE_SEL = 9; //允许的误差大小（选中元素时）
			wf.HOT_SIZE = 12; //热点大小

			//工具函数
			function scale(val) {
				return canvas.scale(val, fnGetScale())
			}

			function ptInLine(pt, start, end) {
				var delta = wf.TOLERANCE_SEL;

				//pt必须在对角线所在矩形内
				var x1 = Math.min(start.x, end.x);
				var x2 = Math.max(start.x, end.x);
				var y1 = Math.min(start.y, end.y);
				var y2 = Math.max(start.y, end.y);
				if (pt.x < x1 - delta / 2 || pt.x > x2 + delta / 2) return false;
				if (pt.y < y1 - delta / 2 || pt.y > y2 + delta / 2) return false;

				//pt在容差范围内必须在直线上

				if (start.x === end.x) {
					if (Math.abs(pt.x - start.x) < delta) {
						var y1 = Math.min(start.y, end.y);
						var y2 = Math.max(start.y, end.y);
						if (pt.y >= y1 && pt.y <= y2) return true;
					}
				} else if (start.y === end.y) {
					if (Math.abs(pt.y - start.y) < delta) {
						var x1 = Math.min(start.x, end.x);
						var x2 = Math.max(start.x, end.x);

						if (pt.x >= x1 && pt.x <= x2) return true;
					}
				} else {
					var dx = (start.x - end.x);
					var dy = (start.y - end.y);
					var k = 1.0 * dy / dx;
					var b = start.y - k * start.x;

					var cy = Math.abs(pt.x * k + b - pt.y);

					if (cy < delta) return true;
				}
				return false;
			}

			function ptInRect(pt, rect) {
				return pt.x >= rect.x && pt.x <= rect.x + rect.width && pt.y >= rect.y && pt.y <= rect.y + rect.height;
			}

			function getDistance(pt1, pt2) {
				var dx = Math.abs(pt1.x - pt2.x);
				var dy = Math.abs(pt1.y - pt2.y);
				return Math.sqrt(dx * dx + dy * dy);
			}
			//得到环节的位置：缩放坐标
			function getProcPosition(proc) {
				var r = proc.position;
				if (proc.type === wf.ProcType.START || proc.type === wf.ProcType.END) {
					var w = Math.min(r.width, r.height);
					var x = r.x + r.width / 2;
					var y = r.y + r.height / 2;
					r = canvas.rect(x - w / 2, y - w / 2, w, w);
				}
				return scale(r);
			}
			//得到环节的热点：缩放坐标
			function getProcHots(proc) {
				var r = getProcPosition(proc);
				var hz = wf.HOT_SIZE;
				return {
					left: canvas.rect(r.x - hz / 2, r.y + r.height / 2 - hz / 2, hz, hz),
					top: canvas.rect(r.x + r.width / 2 - hz / 2, r.y - hz / 2, hz, hz),
					right: canvas.rect(r.x + r.width - hz / 2, r.y + r.height / 2 - hz / 2, hz, hz),
					bottom: canvas.rect(r.x + r.width / 2 - hz / 2, r.y + r.height - hz / 2, hz, hz)
				}
			}
			//得到热点坐标：缩放坐标
			function getHotPosition(proc, hotPos) {
				var r = getProcPosition(proc);
				switch (hotPos) {
					case "left":
						return canvas.point(r.x, r.y + r.height / 2);
					case "top":
						return canvas.point(r.x + r.width / 2, r.y);
					case "right":
						return canvas.point(r.x + r.width, r.y + r.height / 2);
					case "bottom":
						return canvas.point(r.x + r.width / 2, r.y + r.height);
				}
				return null;
			}
			//根据点击位置(缩放坐标)判断所属环节热点
			function hitTestProcHot(proc, pt) {

				var r = getProcPosition(proc);
				var hz = scale(wf.HOT_SIZE);
				var hs = {
					left: canvas.rect(r.x - hz / 2, r.y + r.height / 2 - hz / 2, hz, hz),
					top: canvas.rect(r.x + r.width / 2 - hz / 2, r.y - hz / 2, hz, hz),
					right: canvas.rect(r.x + r.width - hz / 2, r.y + r.height / 2 - hz / 2, hz, hz),
					bottom: canvas.rect(r.x + r.width / 2 - hz / 2, r.y + r.height - hz / 2, hz, hz)
				};

				var hot = null;
				$.each(hs, function (v, k) {
					if (ptInRect(pt, v)) {
						hot = k;
						return true;
					}
				});
				if (hot) return hot;
				if (ptInRect(pt, r)) {
					var delta = [
						getDistance(pt, canvas.point(r.x, r.y + r.height / 2)),
						getDistance(pt, canvas.point(r.x + r.width / 2, r.y)),
						getDistance(pt, canvas.point(r.x + r.width, r.y + r.height / 2)),
						getDistance(pt, canvas.point(r.x + r.width / 2, r.y + r.height))
					];
					var deltaName = ["left", "top", "right", "bottom"];
					var flag = 0;
					var maxLen = Number.MAX_SAFE_INTEGER;
					for (var i = 0; i < delta.length; i++) {
						if (delta[i] < maxLen) {
							flag = i;
							maxLen = delta[i];
						}
					}
					return deltaName[flag];
				}
				return null;
			}

			function getRouteFlag(side) {
				switch (side) {
					case "left":
						return wf.RouteFlag.LEFT;
					case "top":
						return wf.RouteFlag.TOP;
					case "right":
						return wf.RouteFlag.RIGHT;
					case "bottom":
						return wf.RouteFlag.BOTTOM;
				}
				return 0;
			}



			//ID生成器
			wf.IDMaker = function () {
				return {
					id: 0,
					New: function () {
						return ++this.id;
					}
				}
			};

			//数据类型
			wf.DataType = {
				STRING: 0,
				NUMBER: 1,
				FLOAT: 2,
				CURRENCY: 3,
				DATE: 4,
				TIME: 5,
				DATETIME: 6,
                BOOL: 7,
                SYSINFO:8,
				OBJECT: 9,
				ARRAY: 10
			};

			//环节类型
			wf.ProcType = {
				START: 0,
				END: 1,
				EMPTY: 2,
				DECISION: 3,
				SUBCALL: 4,
				PARALLSTART: 5,
				PARALLEND: 6,
				SCRIPT: 7
			};
			wf.ProcTypeName = ["开始环节", "结束环节", "空环节", "人工环节", "连接调用环节", "并行开始环节", "并行结束环节", "脚本环节"];

			function _assign(objTo, objFrom) {
				if (objFrom && $.isObject(objFrom)) {
					$.each(objTo, function (v, k) {
						if (k in objFrom) {
							objTo[k] = objFrom[k]
						}
					});
				}

				return objTo;
			}

			wf.procBase = {
				id: 0,
				isProc: true,
				position: null,
				name: "",
				alias: "",
				icon: null,
				enterscript:"",
				leavescript:""
			};

			wf.procStart = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.START,
					page: {
						id: 0,
						key: "",
						isContainer: true,
						value: {},
						children: [], //所有的实例[{id:0,key:"",value:{},children:[]},...]
					}
				});
				return _assign(p, o);
			};

			wf.procEnd = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.END
				});
				return _assign(p, o);
			};

			wf.procEmpty = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.EMPTY
				});
				return _assign(p, o);
			};

			wf.procDecision = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.DECISION,
					role: "",
					isnotify:false,//是否仅通知
					stop: "",//停止条件
					//cond: "",
					page: {
						id: 0,
						key: "",
						isContainer: true,
						value: {},
						children: [], //所有的实例[{id:0,key:"",value:{},children:[]},...]
					}
				});
				return _assign(p, o);
			};

			wf.procSubCall = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.SUBCALL,
					subname: "",
					subver: 0, //0代表永远取最新版本
					datamapin: "",
					datamapout:"",
					needwait: true
				});
				return _assign(p, o);
			};

			wf.procParallStart = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.PARALLSTART
				});
				return _assign(p, o);
			};

			wf.procParallEnd = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.PARALLEND,
					cond: "",
					stop:""
				});
				return _assign(p, o);
			};

			wf.procScript = function (o) {
				var p = $.extend({}, wf.procBase, {
					type: wf.ProcType.SCRIPT,
					script: ""
				});
				return _assign(p, o);
			};

			wf.RouteFlag = {
				LEFT: 0,
				TOP: 1,
				RIGHT: 2,
				BOTTOM: 3
			};

			wf.RouteStyle = {
				STRAIGH: 0, //直线
				POLYLINE: 1, //折线
				CURVE: 2 //曲线
			};

			wf.route = function (o) {
				var p = {
					id: 0,
					startProc: 0, //起点环节
					endProc: 0, //终点环节
					isRoute: true,
					style: wf.RouteStyle.STRAIGH,
					startFlag: wf.RouteFlag.BOTTOM,
					endFlag: wf.RouteFlag.TOP,
					startPos: null, //起点坐标
					endPos: null, //终点坐标
					cond: "" //路由条件
				};
				return _assign(p, o);
			};



			//文档控制器
			wf.controler = function (doc) {
				return {
					doc: doc,

					onMouseDown: function (e) {
						var me = this;

						var x = e.offsetX;
						var y = e.offsetY;

						var downPos = canvas.point(x, y);
						me.doc.setDownPos(downPos);

						var ht = me.doc.hitTest(downPos);
						if (ht) {
							var downShape = ht.shape;
							var downSide = ht.position;

							if (downSide === "left" || downSide === "right" || downSide === "top" || downSide === "bottom")
								me.doc.setDownHot(ht);
							else {
								me.doc.setDownShape(downShape);
								me.doc.setDownSide(downSide);

								if (!e.shiftKey) {
									if (!me.doc.isSelected(downShape)) {
										me.doc.unselectAll();
									}
								}
								me.doc.select(downShape);
								me.doc.setFocus(downShape);
							}
						} else {
							me.doc.unselectAll();
						}



						me.doc.updateView();
					},
					onMouseMove: function (e) {
						var me = this;

						var x = e.offsetX;
						var y = e.offsetY;

						var movePos = canvas.point(x, y);
						me.doc.setMovePos(movePos);

						if (e.which === 1) {
							var downShape = me.doc.getDownShape();
							if (!downShape) {
								//do nothing
								//暂不实现圈选
							} else {
								if (!me.doc.getDownHot()) {
									var downPos = me.doc.getDownPos();

									var cx = x - downPos.x;
									var cy = y - downPos.y;

									var dx = Math.abs(cx);
									var dy = Math.abs(cy);

									if (dx > wf.TOLERANCE_MOVE || dy > wf.TOLERANCE_MOVE) {
										me.doc.setIsMoving(true);
									} else {
										//误操作 do nothing
									}
								}

							}
						}
						//mouse over
						var hot = null;
						if (e.which === 1) {
							hot = me.doc.getHotShape(movePos);
						}

						if (hot) {

							me.doc.setHot(hot);
							me.doc.setOverShape(null);
						} else {

							me.doc.setHot(null);
							var h = me.doc.hitTest(movePos);
							if (h) {

								if (h.position === "body") {
									me.doc.setOverShape(h.shape);
								}
							} else {
								me.doc.setOverShape(null);
							}
						}

						me.doc.updateView();
					},
					onMouseUp: function (e) {
						var me = this;

						var x = e.offsetX;
						var y = e.offsetY;
						var cx, cy, dx, dy;
						var theScale = fnGetScale();

						var downShape = me.doc.getDownShape();
						if (downShape) {
							var downPos = me.doc.getDownPos();
							var downSide = me.doc.getDownSide();


							cx = x - downPos.x;
							cy = y - downPos.y;

							dx = Math.abs(cx);
							dy = Math.abs(cy);

							if (!(downPos && (dx > wf.TOLERANCE_MOVE || dy > wf.TOLERANCE_MOVE))) {
								//as click
								//do nothing
							} else {
								//as move
								cx /= theScale;
								cy /= theScale;
								if (downSide === "body" || downShape.isProc) {
									//move shape

									$.each(me.doc.getAllSelected(), function (shape) {
										me.doc.moveShape(shape, cx, cy);
									});

									var newSize= me.doc.recalSize();
									newSize.width += 200;
									newSize.height += 200;
									me.doc.setSize(newSize);
								} else {
									//move handler
									if (downShape.isRoute) {
										var route = downShape;

										if (downSide === "start") {
											me.doc.moveRouteStart(route, cx, cy);
										} else if (downSide === "end") {
											me.doc.moveRouteEnd(route, cx, cy);
										} else {

										}
									}
								}
							}
						}
						var downHot = me.doc.getDownHot();
						if (downHot) {
							var hotUp = null;

							$.each(me.doc.getAllProcess(), function (p) {
								var side = hitTestProcHot(p, canvas.point(x, y));
								if (side) {
									hotUp = {
										shape: p,
										position: side
									};
									return true;
								}
							});

							if (hotUp && hotUp.shape.id !== downHot.shape.id) {
								var startPos, endPos;
								startPos = getHotPosition(downHot.shape, downHot.position);
								endPos = getHotPosition(hotUp.shape, hotUp.position);

								startPos.x /= theScale;
								startPos.y /= theScale;
								endPos.x /= theScale;
								endPos.y /= theScale;
								var oRoute = {
									startPos: startPos,
									endPos: endPos,
									startProc: downHot.shape.id,
									endProc: hotUp.shape.id,
									startFlag: getRouteFlag(downHot.position),
									endFlag: getRouteFlag(hotUp.position)
								};

								me.doc.addRoute(wf.route(oRoute));

							}


						}

						me.doc.setDownPos(null);
						me.doc.setDownShape(null);
						me.doc.setDownSide("");
						me.doc.setMovePos(null);
						me.doc.setIsMoving(false);
						me.doc.setOverShape(null);
						me.doc.setHot(null);
						me.doc.setDownHot(null);

						if (me.downShape || me.downHot) {
							me.doc.updateView();
						}

					},
					onKeyDown: function (e) {

						if (e.keyCode === 46) {
							//delete
							this.doc.deleteSelected();
						}
						this.doc.updateView();
					},
					onKeyPress: function (e) {

					},
					onKeyUp: function (e) {
						var me = this;

						var code = e.keyCode;
						if (e.shiftKey && code >= 37 && code <= 40) {
							//37 = left 38 = up 39=right 40=down
							var cx = (code === 37 ? -1 : (code === 39 ? 1 : 0));
							var cy = (code === 38 ? -1 : (code === 40 ? 1 : 0));

							var selected = me.doc.getAllSelected();

							$.each(selected, function (shape) {
								if (shape.isProc) {
									me.doc.moveShape(shape, cx, cy);
								} else if (shape.isRoute) {
									if (!shape.startProc && !shape.endProc) {
										me.doc.moveShape(shape, cx, cy);
									}
								}
							});
							me.doc.updateView();

						}
					},
					RemoveSelections:function () {
						var me = this;
                        me.doc.deleteSelected();
					}
				}
			};

			//文档视图
			wf.view = function () {
				return {
					draw: function (doc) {
						var me = this;

						var dc = fnGetDC();
						if (!dc) return;

						canvas.beginDraw(dc);

						if ($.getConfig().debug) {
							me._draw(doc, dc);
						} else {
							try {
								me._draw(doc, dc);
							} catch (e) {
								$.log("occured when draw workflow,msg:" + e.message)
							}
						}

						canvas.endDraw(dc);
					},
					_draw: function (doc, dc) {
						var me = this;
						me.drawCanvas(doc, dc);

						me.drawOver(doc, dc);
						$.each(doc.getAllProcess(), function (p) {
							me.drawProc(doc, dc, p)
						});
						$.each(doc.getAllRoute(), function (route) {
							me.drawRoute(doc, dc, route)
						});
						me.drawHot(doc, dc);
						me.drawHandler(doc, dc);

						if (doc.getIsMoving() && !doc.getDownHot())
							me.drawMoving(doc, dc);
					},
					//画整体背景
					drawCanvas: function (doc, dc) {
						var docSize = doc.getSize();
						var w = docSize.width;
						var h = docSize.height;
						canvas.fillRectangle(
							dc,
							canvas.brush(canvas.color(255, 255, 255, 255)),
							canvas.rect(0, 0, w, h));
						var i = 0;
						var delta = 30;
						var x = 0;
						var y = 0;
						var crLine = canvas.color(248, 248, 248, 255);
						var pen;
						while (true) {
							i++;
							x += delta;
							if (x > w) break;
							pen = canvas.pen(crLine, 1);
							if (i % 2 === 0) pen.style = canvas.PenStyle.DOT;
							canvas.drawLine(dc, pen, canvas.point(x, 0), canvas.point(x, h));
						}
						i = 0;
						while (true) {
							i++;
							y += delta;
							if (y > h) break;
							pen = canvas.pen(crLine, 1);
							if (i % 2 === 0) pen.style = canvas.PenStyle.DOT;
							canvas.drawLine(dc, pen, canvas.point(0, y), canvas.point(w, y));
						}
					},
					//画选中句柄
					drawHandler: function (doc, dc) {
						var focus = doc.getFocus();
						$.each(doc.getAllSelected(), function (shape) {
							var h = doc.getHandle(shape);
							if (h) {
								$.each(h, function (r, side) {

									var bgCr = canvas.color(241, 241, 241, 220);
									var borderCr = canvas.color(80, 80, 80, 220);
									var bgCrActive = canvas.color(250, 86, 16, 220);
									var borderCrActive = canvas.color(198, 63, 4, 220);

									var br = canvas.brush(shape === focus ? bgCrActive : bgCr);
									var pen = canvas.pen(shape === focus ? borderCrActive : borderCr, 1);

									canvas.fillRectangle(dc, br, r);
									canvas.drawRectangle(dc, pen, r);
								})
							}

						})
					},
					drawProc: function (doc, dc, proc) {
						var checkShape = doc.getCheckShape();
						if (checkShape && checkShape.isProc && checkShape.id === proc.id) {
							var r = getProcPosition(proc);
							r.width += 8;
							r.height += 8;
							r.x -= 4;
							r.y -= 4;
							var sz = scale(canvas.size(5, 5));
							var pen = canvas.pen(canvas.color(255, 0, 0, 200), 2);
							if (proc.type === wf.ProcType.START || proc.type === wf.ProcType.END) {
								canvas.drawCircle(dc, pen, canvas.point(r.x + r.width / 2, r.y + r.height / 2), r.width / 2);
							} else {
								canvas.drawRoundRectangle(dc, pen, r, sz);
							}

						}
						switch (proc.type) {
							case wf.ProcType.START:
								this.drawProcStart(doc, dc, proc);
								break;
							case wf.ProcType.END:
								this.drawProcEnd(doc, dc, proc);
								break;
							case wf.ProcType.EMPTY:
								this.drawProcEmpty(doc, dc, proc);
								break;
							case wf.ProcType.DECISION:
								this.drawProcDecision(doc, dc, proc);
								break;
							case wf.ProcType.SUBCALL:
								this.drawProcSubCall(doc, dc, proc);
								break;
							case wf.ProcType.PARALLSTART:
								this.drawProcParallStart(doc, dc, proc);
								break;
							case wf.ProcType.PARALLEND:
								this.drawProcParallEnd(doc, dc, proc);
								break;
							case wf.ProcType.SCRIPT:
								this.drawProcScript(doc, dc, proc);
								break;
							default:
								break;
						}

					},
					drawProcStart: function (doc, dc, proc) {
						var r = getProcPosition(proc);
						var w = r.width;

						var br = canvas.brush(canvas.color(34, 169, 5, 200));
						var pen = canvas.pen(canvas.color(27, 130, 4), 1);

						canvas.fillCircle(dc, br, canvas.point(r.x + r.width / 2, r.y + r.height / 2), w / 2);
						canvas.drawCircle(dc, pen, canvas.point(r.x + r.width / 2, r.y + r.height / 2), w / 2);

						var fnt = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fnt.color = canvas.color(255, 255, 255, 255);

						canvas.drawString(
							dc,
							proc.alias,
							canvas.rect(r.x + r.width / 2 - w / 2, r.y + r.height / 2 - w / 2, w, w),
							fnt,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcEnd: function (doc, dc, proc) {
						var r = getProcPosition(proc);
						var w = r.width;

						var br = canvas.brush(canvas.color(226, 18, 1, 200));
						var pen = canvas.pen(canvas.color(205, 16, 1), 1);
						var pc = canvas.point(r.x + r.width / 2, r.y + r.height / 2);

						canvas.fillCircle(dc, br, pc, w / 2);
						canvas.drawCircle(dc, pen, pc, w / 2);

						var fnt = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fnt.color = canvas.color(255, 255, 255, 255);

						canvas.drawString(
							dc,
							proc.alias,
							canvas.rect(pc.x - w / 2, pc.y - w / 2, w, w),
							fnt,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcDecision: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(249, 224, 198, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(234, 154, 66, 200), 1);
						var penLine = canvas.pen(canvas.color(245, 208, 167, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(
							dc,
							penLine,
							canvas.point(r.x, r.y + r.height / 2),
							canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcSubCall: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(222, 236, 237, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(117, 177, 183, 200), 1);
						var penLine = canvas.pen(canvas.color(172, 207, 210, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(dc, penLine, canvas.point(r.x, r.y + r.height / 2), canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcScript: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(239, 235, 205, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(205, 192, 99, 200), 1);
						var penLine = canvas.pen(canvas.color(224, 217, 160, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(dc, penLine, canvas.point(r.x, r.y + r.height / 2), canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcEmpty: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(249, 251, 250, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(112, 173, 153, 200), 1);
						var penLine = canvas.pen(canvas.color(186, 216, 206, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(dc, penLine, canvas.point(r.x, r.y + r.height / 2), canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);


					},
					drawProcParallStart: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(223, 224, 235, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(166, 132, 174, 200), 1);
						var penLine = canvas.pen(canvas.color(207, 188, 211, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(dc, penLine, canvas.point(r.x, r.y + r.height / 2), canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawProcParallEnd: function (doc, dc, proc) {
						var r = getProcPosition(proc);

						var br = canvas.brush(canvas.color(227, 204, 247, 200));
						br.style = canvas.BrushStyle.LINEAR_GRADIENT;
						br.colorEnd = canvas.color(255, 255, 255, 200);
						br.start = canvas.point(r.x, r.y);
						br.end = canvas.point(r.x, r.y + r.height);

						var pen = canvas.pen(canvas.color(151, 66, 221, 200), 1);
						var penLine = canvas.pen(canvas.color(198, 150, 237, 200), 1);
						var sz = scale(canvas.size(5, 5));

						canvas.fillRoundRectangle(dc, br, r, sz);
						canvas.drawRoundRectangle(dc, pen, r, sz);

						//中间分割线
						penLine.style = canvas.PenStyle.DASH;
						penLine.dash = [7, 5];
						canvas.drawLine(dc, penLine, canvas.point(r.x, r.y + r.height / 2), canvas.point(r.x + r.width, r.y + r.height / 2));

						//图标与环节类型
						var iconSize = 16;
						if (proc.icon) {
							canvas.drawImage(dc, proc.icon,
								r.x + 10, r.y + r.height / 4 - 16 / 2, 16, 16,
								0, 0, proc.icon.width, proc.icon.height
							);
						}
						var typeName = wf.ProcTypeName[proc.type];

						var fntTypeName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntTypeName.color = canvas.color(52, 92, 80, 255);
						canvas.drawString(dc,
							typeName,
							canvas.rect(r.x, r.y, r.width, r.height / 2),
							fntTypeName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);

						//环节名称
						var fntName = canvas.font("宋体", 12, canvas.FontStyle.REGULAR);
						fntName.color = canvas.color(38, 29, 209, 255);
						canvas.drawString(dc,
							proc.name,
							canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
							fntName,
							canvas.TextAlign.CENTER,
							canvas.TextAlign.CENTER
						);
					},
					drawRoute: function (doc, dc, route) {
						var ptStart = scale(route.startPos);
						var ptEnd = scale(route.endPos);

						var pen = canvas.pen(route.cond ? canvas.color(40, 44, 52, 220) : canvas.color(237, 12, 40, 220), 1);
						if (!(route.startProc && route.endProc)) {
							pen.style = canvas.PenStyle.DASH;
							pen.dash = [6, 4];
						}

						switch (route.style) {
							case wf.RouteStyle.STRAIGH:
								canvas.drawLine(dc, pen, ptStart, ptEnd);
								canvas.drawArrow(dc, pen, ptStart, ptEnd);
								break;
							case wf.RouteStyle.POLYLINE:
								break;
							case wf.RouteStyle.CURVE:
								break;
						}
					},
					drawMoving: function (doc, dc) {
						var me = this;
						if (!doc.movePos || !doc.downPos) return;
						var cx = doc.movePos.x - doc.downPos.x;
						var cy = doc.movePos.y - doc.downPos.y;

						if (Math.abs(cx) > wf.TOLERANCE_MOVE || Math.abs(cy) > wf.TOLERANCE_MOVE) {
							$.each(doc.getAllSelected(), function (shape) {
								if (shape.isProc) {
									var r = getProcPosition(shape);
									r.x += cx;
									r.y += cy;

									var w = r.width;

									var br = canvas.brush(canvas.color(244, 244, 244, 180));
									var pen = canvas.pen(canvas.color(117, 177, 183, 200), 1);
									pen.style = canvas.PenStyle.DASH;
									pen.dash = [7, 2];

									if (shape.type === wf.ProcType.START || shape.type === wf.ProcType.END) {
										canvas.fillCircle(dc, br, canvas.point(r.x + r.width / 2, r.y + r.height / 2), w / 2)
										canvas.drawCircle(dc, pen, canvas.point(r.x + r.width / 2, r.y + r.height / 2), w / 2);
									} else {
										var sz = scale(canvas.size(5, 5));

										canvas.fillRoundRectangle(dc, br, r, sz);
										canvas.drawRoundRectangle(dc, pen, r, sz);
									}


									//环节名称
									var fntName = canvas.font("宋体", 18, canvas.FontStyle.REGULAR);
									fntName.color = canvas.color(38, 29, 209, 255);
									canvas.drawString(dc,
										shape.alias,
										r, //canvas.rect(r.x, r.y + r.height / 2, r.width, r.height / 2),
										fntName,
										canvas.TextAlign.CENTER,
										canvas.TextAlign.CENTER
									);
								} else if (shape.isRoute) {
									var startPos = scale(shape.startPos);
									var endPos = scale(shape.endPos);
									if (doc.downSide === "start") {
										startPos.x += cx;
										startPos.y += cy;
									} else if (doc.downSide === "end") {
										endPos.x += cx;
										endPos.y += cy;
									} else {
										startPos.x += cx;
										startPos.y += cy;
										endPos.x += cx;
										endPos.y += cy;
									}


									var pen = canvas.pen(canvas.color(40, 44, 52, 220), 1);
									pen.style = canvas.PenStyle.DASH;
									pen.dash = [2, 2];

									switch (shape.style) {
										case wf.RouteStyle.STRAIGH:
											canvas.drawLine(dc, pen, startPos, endPos);
											canvas.drawArrow(dc, pen, startPos, endPos);
											break;
										case wf.RouteStyle.POLYLINE:
											break;
										case wf.RouteStyle.CURVE:
											break;
									}
								}

							})
						}


					},
					drawOver: function (doc, dc) {
						var overShape = doc.getOverShape();
						if (overShape) {

							if (overShape.isProc) {
								var proc = overShape;
								var r = getProcPosition(proc);
								r.width += 4;
								r.height += 4;
								r.x -= 2;
								r.y -= 2;

								var br = canvas.brush(canvas.color(150, 135, 250, 200));

								if (proc.type === wf.ProcType.START || proc.type === wf.ProcType.END) {
									br.style = canvas.BrushStyle.RADIAL_GRADIENT;
									br.color = canvas.color(104, 84, 248, 200);
									br.colorEnd = canvas.color(218, 213, 253, 200);
									br.start = canvas.point(r.x + r.width / 2, r.y + r.height / 2);
									br.end = br.start;
									br.rStart = 0;
									br.rEnd = r.width / 2;
									canvas.fillCircle(dc, br, canvas.point(r.x + r.width / 2, r.y + r.height / 2), r.width / 2);
								} else {
									var sz = scale(canvas.size(5, 5));
									canvas.fillRoundRectangle(dc, br, r, sz);
								}

							} else if (overShape.isRoute) {
								var route = overShape;

								var ptStart = scale(route.startPos);
								var ptEnd = scale(route.endPos);

								var pen = canvas.pen(canvas.color(150, 135, 250, 200), 3);
								if (!(route.startProc && route.endProc)) {
									pen.style = canvas.PenStyle.DASH;
									pen.dash = [6, 4];
								}

								switch (route.style) {
									case wf.RouteStyle.STRAIGH:
										canvas.drawLine(dc, pen, ptStart, ptEnd);
										canvas.drawArrow(dc, pen, ptStart, ptEnd);
										break;
									case wf.RouteStyle.POLYLINE:
										break;
									case wf.RouteStyle.CURVE:
										break;
								}
							}

						}
					},
					drawHot: function (doc, dc) {
						var movePos = doc.getMovePos();
						if (!movePos) return;
						var hot = doc.getHotShape(doc.getMovePos());
						if (hot) {
							var shape = hot.shape;
							var position = hot.position;
							if (shape.isProc) {
								var r = getProcPosition(shape);
								var c = null;
								switch (position) {
									case "left":
										c = canvas.point(r.x, r.y + r.height / 2);
										break;
									case "top":
										c = canvas.point(r.x + r.width / 2, r.y);
										break;
									case "right":
										c = canvas.point(r.x + r.width, r.y + r.height / 2);
										break;
									case "bottom":
										c = canvas.point(r.x + r.width / 2, r.y + r.height);
										break;

								}
								if (c) {
									var br = canvas.brush(canvas.color(220, 52, 33, 200));
									br.style = canvas.BrushStyle.RADIAL_GRADIENT;
									br.colorEnd = canvas.color(235, 105, 90, 200);
									br.start = c;
									br.end = br.start;
									br.rStart = 0;
									br.rEnd = 8;
									canvas.fillCircle(dc, br, c, 8);
								}
							}

						}
						var downHot = doc.getDownHot();
						if (downHot) {
							var startPos;
							var r = getProcPosition(downHot.shape);
							switch (downHot.position) {
								case "left":
									startPos = canvas.point(r.x, r.y + r.height / 2);
									break;
								case "top":
									startPos = canvas.point(r.x + r.width / 2, r.y);
									break;
								case "right":
									startPos = canvas.point(r.x + r.width, r.y + r.height / 2);
									break;
								case "bottom":
									startPos = canvas.point(r.x + r.width / 2, r.y + r.height);
									break;
								default:
									break;
							}
							var endPos = movePos;

							var pen = canvas.pen(canvas.color(40, 44, 52, 220), 1);
							pen.style = canvas.PenStyle.DASH;
							pen.dash = [2, 2];
							canvas.drawLine(dc, pen, startPos, endPos);
							canvas.drawArrow(dc, pen, startPos, endPos);

						}
					}
				}
			};

			//action:路由与环节的关联
			wf.actionAnchor = function () {
				return {
					event: function (doc, evtName) {

						if (evtName === 'shapeAnchorChange') {
							var route = arguments[2];
							var pos = arguments[3];

							if (route && route.isRoute && (pos === "start" || pos === "end")) {
								//路由锚点改变了，则重新计算锚点
								var posKey = "" + pos + "Pos";
								var procKey = "" + pos + "Proc";
								var flagKey = "" + pos + "Flag";

								var pt = route[posKey];

								var hot = this.findHot(doc, pt);

								if (hot) {

									//更新route的位置
									var targetid = hot.shape.id;
									//起点和终点不可以在同一个环节
									if (pos === "start" && route.endProc !== targetid || pos === "end" && route.startProc !== targetid) {
										route[procKey] = hot.shape.id;

										var newPos = null;
										var r = hot.shape.position;
										if (hot.position === "left") {
											newPos = canvas.point(r.x, r.y + r.height / 2);
											route[flagKey] = wf.RouteFlag.LEFT;
										} else if (hot.position === "top") {
											newPos = canvas.point(r.x + r.width / 2, r.y);
											route[flagKey] = wf.RouteFlag.TOP;
										} else if (hot.position === "right") {
											newPos = canvas.point(r.x + r.width, r.y + r.height / 2);
											route[flagKey] = wf.RouteFlag.RIGHT;
										} else if (hot.position === "bottom") {
											newPos = canvas.point(r.x + r.width / 2, r.y + r.height);
											route[flagKey] = wf.RouteFlag.BOTTOM;
										}

										route[posKey] = newPos;
									}

								} else {
									route[procKey] = 0;
									route[flagKey] = wf.RouteFlag.BOTTOM;
								}
							}
						} else if (evtName === "shapePosChange") {
							var shape = arguments[2];
							if (shape.isProc) {
								var proc = shape;
								var r = proc.position;

								//更新所有连接到这个环节的路由坐标
								$.each(doc.getAllRoute(), function (route) {
									var pk = "";
									if (route.startProc === proc.id) {
										pk = "start"
									}
									if (route.endProc === proc.id) {
										pk = "end"
									}

									var k = pk + "Pos";
									switch (route[pk + "Flag"]) {
										case wf.RouteFlag.LEFT:
											route[k] = canvas.point(r.x, r.y + r.height / 2);
											break;
										case wf.RouteFlag.TOP:
											route[k] = canvas.point(r.x + r.width / 2, r.y);
											break;
										case wf.RouteFlag.RIGHT:
											route[k] = canvas.point(r.x + r.width, r.y + r.height / 2);
											break;
										case wf.RouteFlag.BOTTOM:
											route[k] = canvas.point(r.x + r.width / 2, r.y + r.height);
											break;
									}
								})
							} else if (shape.isRoute) {
								var route = shape;

								var pt = route.startPos;
								var hot = this.findHot(doc, pt);
								var r;
								if (hot) {
									route.startProc = hot.shape.id;
									r = hot.shape.position;
									switch (hot.position) {
										case "left":
											route.startFlag = wf.RouteFlag.LEFT;
											route.startPos = canvas.point(r.x, r.y + r.height / 2);
											break;
										case "top":
											route.startFlag = wf.RouteFlag.TOP;
											route.startPos = canvas.point(r.x + r.width / 2, r.y);
											break;
										case "right":
											route.startFlag = wf.RouteFlag.RIGHT;
											route.startPos = canvas.point(r.x + r.width, r.y + r.height / 2);
											break;
										case "bottom":
											route.startFlag = wf.RouteFlag.BOTTOM;
											route.startPos = canvas.point(r.x + r.width / 2, r.y + r.height);
											break;
									}
								} else {
									route.startProc = 0;
								}

								pt = route.endPos;
								hot = this.findHot(doc, pt);
								if (hot) {
									route.endProc = hot.shape.id;
									r = hot.shape.position;
									switch (hot.position) {
										case "left":
											route.endFlag = wf.RouteFlag.LEFT;
											route.endPos = canvas.point(r.x, r.y + r.height / 2);
											break;
										case "top":
											route.endFlag = wf.RouteFlag.TOP;
											route.endPos = canvas.point(r.x + r.width / 2, r.y);
											break;
										case "right":
											route.endFlag = wf.RouteFlag.RIGHT;
											route.endPos = canvas.point(r.x + r.width, r.y + r.height / 2);
											break;
										case "bottom":
											route.endFlag = wf.RouteFlag.BOTTOM;
											route.endPos = canvas.point(r.x + r.width / 2, r.y + r.height);
											break;
									}
								} else {
									route.endProc = 0;
								}

								if (route.startProc && route.endProc && route.startProc === route.endProc) {
									route.endProc = 0; //避免指向同一环节
								}
							}
						} else if (evtName === "shapeRemove") {
							var shape = arguments[2];
							if (shape.isProc) {
								$.each(doc.getAllRoute(), function (route) {
									if (route.startProc === shape.id) {
										route.startProc = 0;
									}
									if (route.endProc === shape.id) {
										route.endProc = 0;
									}
								})
							}
						}
					},
					findHot: function (doc, pt) {
						pt = scale(pt);
						var hot = null;
						$.each(doc.getAllProcess(), function (proc) {
							//先判断是否在hot中
							var side = hitTestProcHot(proc, pt);
							if (side) {
								hot = {
									shape: proc,
									position: side
								};
								return true;
							}
						});
						return hot;
					}
				}
			};

			/*
			* 流程文档
				存储工作流数据，比如环节、路由；当数据变化时通过事件通知订阅者
			主要事件：
				事件名称				描述					参数
				-------------------------------------------------------------------------------------------
				shapeCreate			元素创建				(shape)
				shapeRemove			元素删除				(shape)
				shapePropChange		元素属性改变			(shape,field)
				shapePosChange		元素位置改变			(shape)
				shapeAnchorChange	路由锚点改变			(shape,side)
				shapeSelect			选中元素				(shape)
				shapeUnselect		取消选中				(shape)
				shapeUnselectAll	取消选中所有			()
				shapeFocus			焦点改变				(oldShape,newShape)
				canvasSizeChange	画布大小改变			(width,height)
				-------------------------------------------------------------------------------------------
			*
			* */
			wf.doc = function () {
				return {
					processes: [],
					routes: [],
					datavars: [],
					minwidth: 1024,
					minheight: 1024,
					width: 1024, //当前宽度
					height: 1024, //当前高度
					idMaker: wf.IDMaker(),
					selected: [], //所有选中的元素
					focus: null, //当前焦点
					actions: [], //action必须有函数：function event(doc,evtName,args...){}
					isDirty: true, //是否需要重绘
					view: null,
					hot: null, //鼠标当前所在热点


					downPos: null, //mousedown point
					downShape: null, //mousedown shape
					downSide: "", //mousedown side
					downHot: null,
					movePos: null, //mousemove point
					overShape: null, //mouse over shape

					isMoving: false,

					checkShape: null,


					//==== 信息获取接口[begin] =======
					getAllProcess: function () {
						return this.processes;
					},
					getAllRoute: function () {
						return this.routes;
					},
					getAllData: function () {
						return this.datavars;
					},
					getFocus: function () {
						return this.focus;
					},
					getAllSelected: function () {
						return this.selected;
					},
					isSelected: function (shape) {
						var me = this;
						if (!shape) return false;
						var index = $.indexOfArray(shape, me.selected, function (a, b) {
							return a.id === b.id
						});
						return index >= 0;
					},
					getDownPos: function () {
						return this.downPos;
					},
					getDownShape: function () {
						return this.downShape;
					},
					getDownSide: function () {
						return this.downSide;
					},
					getDownHot: function () {
						return this.downHot;
					},
					getMovePos: function () {
						return this.movePos;
					},
					getIsMoving: function () {
						return this.isMoving;
					},
					getHot: function () {
						return this.hot;
					},
					getOverShape: function () {
						return this.overShape;
					},
					getCheckShape: function () {
						return this.checkShape
					},
					getSize: function () {
						return canvas.size(this.width, this.height);
					},
					findProc: function (id) {
						var p = null;
						$.each(this.processes, function (proc) {
							if (proc.id === id) {
								p = proc;
								return true;
							}
						});
						return p;
					},
					//==== 信息获取接口[end] =======

					//==== 文档操作[begin] =======
					setFocus: function (shape) {
						var me = this;
						var focus = me.focus;
						if (focus !== shape) {
							me.focus = shape;
							me.notify("shapeFocus", focus, shape);
							me.isDirty = true;
						}
					},
					setView: function (oView) {
						this.view = oView;
					},
					setMinSize: function (sz) {
						var me = this;
						me.minwidth = sz.width;
						me.minheight = sz.height;
						//重计算大小
						me.setSize(me.recalSize());
					},
					setSize: function (sz) {
						var me = this;
						me.width = sz.width;
						me.height = sz.height;
						me.isDirty = true;
						fnDocSizeChange(sz, function () {
							me.updateView();
						})
					},
					setHot: function (hot) {
						var h = this.hot;
						if (h || hot) {
							if (h && hot && (h.shape === hot.shape && h.position === hot.position)) {
								//is same, do nothing
							} else {
								this.hot = hot;
								this.isDirty = true;
							}
						}
					},
					setDownPos: function (pos) {
						this.downPos = pos;
					},
					setDownHot: function (hot) {
						this.downHot = hot;
						this.isDirty = true;
					},
					setDownShape: function (shape) {
						this.downShape = shape;
					},
					setDownSide: function (side) {
						this.downSide = side;
					},
					setOverShape: function (shape) {
						if (this.overShape !== shape) {
							this.overShape = shape;
							this.isDirty = true;
						}
					},
					setCheckShape: function (shape) {
						this.checkShape = shape;
						this.isDirty = true;
					},
					setIsMoving: function (isMoving) {
						if (!this.isMoving) {
							this.isDirty = true;
							this.isMoving = isMoving;
						}
					},
					setMovePos: function (pos) {
						var v = this.movePos;
						if (v && pos && v.x === pos.x && v.y === pos.y) {
							//is same
						} else {
							this.movePos = pos;
							this.isDirty = true;
						}

					},
					select: function (shape) {
						var me = this;
						if (!me.isSelected(shape)) {
							me.selected.push(shape);
							me.notify("shapeSelect", shape);
							me.isDirty = true;
						}
					},
					unselect: function (shape) {
						var me = this;
						if (!shape) return;
						var index = $.indexOfArray(shape, me.selected, function (a, b) {
							return a.id === b.id
						});
						if (index >= 0) {
							me.isDirty = true;
							me.selected.splice(index, 1);
							me.notify("shapeUnselect", shape);
							if (shape === me.focus) {
								var focus = null;
								if (me.selected.length > index) {
									focus = me.selected[index];
								} else if (me.selected.length) {
									focus = me.selected[me.selected.length - 1];
								}
								me.setFocus(focus);
							}
						}
					},
					unselectAll: function () {
						var me = this;
						if (me.selected.length > 0) {
							me.isDirty = true;
							me.selected = [];
							me.setFocus(null);
							me.notify("shapeUnselectAll");
						}
					},
					moveShape: function (shape, cx, cy) {
						var me = this;
						if (!(cx === 0 && cy === 0)) {
							if (shape.isProc) {
								shape.position.x += cx;
								shape.position.y += cy;
							} else if (shape.isRoute) {
								shape.startPos = canvas.point(shape.startPos.x + cx, shape.startPos.y + cy);
								shape.endPos = canvas.point(shape.endPos.x + cx, shape.endPos.y + cy);
							}
							me.isDirty = true;
							me.notify("shapePosChange", shape);
						}

					},
					moveRouteStart: function (route, cx, cy) {
						var me = this;
						if (route.isRoute) {
							route.startPos.x += cx;
							route.startPos.y += cy;
							me.isDirty = true;
							me.notify("shapeAnchorChange", route, "start");
						}
					},
					moveRouteEnd: function (route, cx, cy) {
						var me = this;
						if (route.isRoute) {
							route.endPos.x += cx;
							route.endPos.y += cy;
							me.isDirty = true;
							me.notify("shapeAnchorChange", route, "end");
						}
					},
					deleteSelected: function () {
						var me = this;
						$.each(me.selected, function (shape) {
							var index;
							if (shape.isProc) {
								index = $.indexOfArray(shape, me.processes, function (a, b) {
									return a.id === b.id;
								});
								if (index >= 0) me.processes.splice(index, 1);
							} else if (shape.isRoute) {
								index = $.indexOfArray(shape, me.routes, function (a, b) {
									return a.id === b.id;
								});
								if (index >= 0) me.routes.splice(index, 1);
							}
							me.notify("shapeRemove", shape);
						});
						me.isDirty = true;
						me.unselectAll();
						me.setFocus(null);
					},
					//==== 文档操作[end] =======

					//事件触发
					notify: function () {
						var me = this;
						var arr = [me];
						for (var i = 0; i < arguments.length; i++) {
							arr.push(arguments[i]);
						}

						$.each(this.actions, function (action) {
							var fn = action.event;
							if ($.isFunction(fn)) {
								fn.apply(action, arr)
							}
						})
					},
					//更新视图
					updateView: function () {
						var me = this;
						if (me.isDirty) {
							me.isDirty = false;
							if (me.view) {
								me.view.draw(me);
							}
						}
					},
					//注册action
					registerAction: function (a) {
						this.actions.push(a)
					},

					//持久化
					serial: function () {
						var obj = {
							processes: this.processes,
							routes: this.routes,
							datavars: this.datavars
						};
						return $.toJson(obj);
					},
					unserial: function (str) {
						var me = this;
						var obj = $.fromJson(str);
						if (!obj) obj = {};

						me.processes = [];
						me.routes = [];
						me.datavars = [];

						//reset ids
						var pid = me.idMaker;
						pid.id = 0; //reset

						$.each(obj.processes, function (p) {
							me.processes.push(p);
							if (p.id > pid.id) {
								pid.id = p.id;
							}
						});
						$.each(obj.routes, function (r) {
							me.routes.push(r);
							if (r.id > pid.id) {
								pid.id = r.id;
							}
						});
						$.each(obj.datavars, function (d) {
							me.datavars.push(d);
							if (d.id > pid.id) {
								pid.id = d.id;
							}
						});
					},

					//其它工具函数

					hitTest: function (pt) {
						var me = this;
						var t = null;

						var focus = me.getFocus(0);
						if (focus) {
							t = me.hitTestShape(pt, focus);
							if (t) return t;
						}
						var selected = me.getAllSelected();
						var count = selected.length;
						var i = 0;
						for (i = 0; i < count; i++) {
							t = me.hitTestShape(pt, selected[i]);
							if (t) return t;
						}

						t = me.getHotShape(pt);
						if (t) return t;

						var routes = me.getAllRoute();
						count = routes.length;
						for (i = 0; i < count; i++) {
							t = me.hitTestShape(pt, routes[i]);
							if (t) return t;
						}

						var processes = me.getAllProcess();
						count = processes.length;
						for (i = 0; i < count; i++) {
							t = me.hitTestShape(pt, processes[i]);
							if (t) return t;
						}

						return null;
					},
					hitTestShape: function (pt, shape) {
						var me = this;

						if (me.isSelected(shape)) {
							var hShape = me.getHandle(shape);
							if (hShape) {
								var found = null;
								$.each(hShape, function (r, k) {
									//$.log("hit on "+shape.id+" " +k+ " pt:"+$.toJson(pt)+" r="+$.toJson(r));

									if (ptInRect(pt, r)) {
										found = {
											shape: shape,
											position: k
										};
										return true;
									}
								});
								if (found) return found;
							}
						}


						if (shape.isRoute) {
							var start = scale(shape.startPos);
							var end = scale(shape.endPos);
							if (ptInLine(pt, start, end)) {

								return {
									shape: shape,
									position: "body"
								}
							}
						}
						if (shape.isProc) {
							var rBody = getProcPosition(shape);
							if (ptInRect(pt, rBody)) {
								return {
									shape: shape,
									position: "body"
								}
							}
						}
					},

					getHotShape: function (pt) {
						var me = this;

						var hot = null;
						$.each(me.getAllProcess(), function (proc) {
							var hots = getProcHots(proc);
							$.each(hots, function (r, side) {
								if (ptInRect(pt, r)) {
									hot = {
										shape: proc,
										position: side
									};
									return true;
								}
							});
							if (hot) return true;
						});
						return hot;
					},
					addProc: function (p) {
						p.id = this.idMaker.New();
						this.processes.push(p);
						return p;
					},
					addRoute: function (r) {
						r.id = this.idMaker.New();
						this.routes.push(r);
						return r;
					},
					addData: function (d) {
						d.id = this.idMaker.New();
						this.datavars.push(d);
						return d;
					},

					newProcName: function (pre) {
						var me = this;
						pre = pre || "新建环节";
						var i = 1;
						var name = "";
						while (true) {
							name = pre + i;
							var found = false;
							$.each(me.processes, function (p) {
								if (p.name === name) {
									found = true;
									return true;
								}
							});
							if (!found) break;
							i++;
						}
						return name;
					},
					recalSize: function () {
						var me = this;
						var size = canvas.size(0, 0);
						$.each(me.processes, function (p) {
							var pos = p.position;

							if (pos.x + pos.width > size.width) size.width = pos.x + pos.width;
							if (pos.y + pos.height > size.height) size.height = pos.y + pos.height;
						});

						size = scale(size);

						if (size.width < me.minwidth) size.width = me.minwidth;
						if (size.height < me.minheight) size.height = me.minheight;

						return size;
					},

					getHandle: function (shape) {
						var me = this;
						var hw = scale(wf.HANDLE_WIDTH);

						if (shape.isRoute) {
							var startPos = scale(shape.startPos);
							var endPos = scale(shape.endPos);

							return {
								start: canvas.rect(startPos.x - hw / 2, startPos.y - hw / 2, hw, hw),
								end: canvas.rect(endPos.x - hw / 2, endPos.y - hw / 2, hw, hw)
							}
						} else if (shape.isProc) {
							var r = getProcPosition(shape);

							return {
								LeftTop: canvas.rect(r.x - hw, r.y - hw, hw, hw),
								RightTop: canvas.rect(r.x + r.width, r.y - hw, hw, hw),
								LeftBottom: canvas.rect(r.x - hw, r.y + r.height, hw, hw),
								RightBottom: canvas.rect(r.x + r.width, r.y + r.height, hw, hw)
							}
						}
					},
					newID: function () {
						return this.idMaker.New();
					}

				}
			};

			wf.checker = function (doc, app) {
				return {
					doc: doc,
					app: app,
					errors: [],
					chars: ["/", "\\", ":", "!", "@", "#", "&", "+", "-", "*", "%", "^", "~", "[", "]", "{", "}", "?", "<", ">", "=", ",", ".", ";", "'", '"', "(", ")", " ", "\r", "\n", "\t"],
					numbers: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
					check: function (fnOK, fnFail) {
						var me = this;

						me.checkApp();
						$.each(me.doc.getAllProcess(), function (p) {
							me.checkProc(p)
						});
						$.each(me.doc.getAllRoute(), function (r) {
							me.checkRoute(r)
						});
						$.each(me.doc.getAllData(), function (v) {
							me.checkDataVar(v)
						});

						var errCount = 0;
						$.each(me.errors, function (e) {
							if (e.type === "error") {
								errCount++;
							}
						});
						if (errCount) {
							if ($.isFunction(fnFail)) fnFail(me.errors);
						} else {
							if ($.isFunction(fnOK)) fnOK(me.errors);
						}

					},
					checkApp: function () {
						var me = this;
						me._isValidName(me.app.name, "流程名称", me.app);
						me._isValidName(me.app.alias, "流程显示名", me.app);
						//fix me later : 检查流程前置脚本
					},
					checkProc: function (p) {
						var me = this;
						me._isValidName(p.name, "环节名称", p);
						me._isValidName(p.alias, "环节显示名", p);
						me._isSameProcName(p.name, "环节名称", p);

						var pt = wf.ProcType;

						//结束环节不可以有流出路由
						if (p.type === wf.ProcType.END) {
							$.each(me.doc.getAllRoute(), function (r) {
								if (r.startProc === p.id) {
									me.errors.push({
										type: "error",
										shape: p,
										message: "结束环节不可以有流出路由"
									})
									return true;
								}
							})
						}
						//开始环节不可以流入路由
						if (p.type === wf.ProcType.START) {
							$.each(me.doc.getAllRoute(), function (r) {
								if (r.endProc === p.id) {
									me.errors.push({
										type: "error",
										shape: p,
										message: "开始环节不可以有流入路由"
									})
									return true;
								}
							})
						}
						//不可以有孤立的环节
						var rFlowCount = 0;
						$.each(me.doc.getAllRoute(), function (r) {
							if (r.endProc === p.id || r.startProc === p.id) {
								rFlowCount++;
							}
						});
						if (!rFlowCount) {
							me.errors.push({
								type: "error",
								shape: p,
								message: "环节" + p.name + "是孤立环节"
							})
						}

						//环节不可以有多条缺省路由
						if (p.type !== pt.END) {
							var defCount = 0;
							$.each(me.doc.getAllRoute(), function (r) {
								if (r.startProc === p.id) {
									if (!r.cond) defCount++;
								}
							});
							if (defCount > 1) {
								if (p.type !== pt.PARALLSTART) {//只有并行开始可以有多条缺省路由
									me.errors.push({
										type: "error",
										shape: p,
										message: "环节" + p.name + "有多条缺省路由"
									})
								}

							} else if (defCount < 1) {
								me.errors.push({
									type: "warning",
									shape: p,
									message: "环节" + p.name + "缺少缺省路由"
								});
							}
						}


						switch (p.type) {
							case pt.START:
								if (!p.page) {
									me.errors.push({
										type: "warning",
										shape: p,
										message: "开始环节缺少页面定义"
									})
								}
								break;
							case pt.END:
								break;
							case pt.EMPTY:
								break;
							case pt.DECISION:
								if (!p.role) {
									me.errors.push({
										type: "warning",
										shape: p,
										message: "环节" + p.name + "缺少角色定义"
									})
								}
								if (!p.page) {
									me.errors.push({
										type: "error",
										shape: p,
										message: "环节" + p.name + "缺少页面定义"
									})
								}
								break;
							case pt.SUBCALL:
								if (!p.subname) {
									me.errors.push({
										type: "error",
										shape: p,
										message: "环节" + p.name + "缺少子流程名"
									})
								}
								break;
							case pt.PARALLSTART:
								break;
							case pt.PARALLEND:
								break;
							case pt.SCRIPT:
								break;
							default:
								break;
						}
					},
					checkRoute: function (r) {
						var me = this;
						if (r.startProc) {
							if (!me.doc.findProc(r.startProc)) {
								me.errors.push({
									type: "error",
									shape: r,
									message: "路由的起点环节不存在(内部错误)"
								})
							}
						} else {
							me.errors.push({
								type: "error",
								shape: r,
								message: "未连接的路由，缺少起点环节"
							})
						}
						if (r.endProc) {
							if (!me.doc.findProc(r.endProc)) {
								me.errors.push({
									type: "error",
									shape: r,
									message: "路由的终点环节不存在(内部错误)"
								})
							}
						} else {
							me.errors.push({
								type: "error",
								shape: r,
								message: "未连接的路由，缺少终点环节"
							})
						}
					},
					checkDataVar: function (v) {
						var me = this;
						if (v.name === "appid" || v.name === "jobid") {
							me.errors.push({
								type: "error",
								shape: v,
								message: "数据" + v.name + "的名称不可以是保留名字"
							})
						}
						me._isValidName(v.name, "数据名", v);
						me._isValidName(v.alias, "数据显示名", v);
						
						if( v.type === wf.DataType.SYSINFO ){
							if( !v.sysinfopath ){
								me.errors.push({
									type: "error",
									shape: v,
									message: "数据" + v.name + "没选择系统信息路径"
								})
							}
						}
						//变量不可以是预定义参数名
						$.each(["appid", "verid", "appname", "jobid", "taskid", "workid"], function (name) {
							if (v.name === name) {
								me.errors.push({
									type: "error",
									shape: v,
									message: "数据" + v.name + "名称不可以是预定义参数名称"
								})
							}
						})
					},
					_isValidName: function (name, hint, shape) {
						var me = this;
						if (name.length < 1) {
							me.errors.push({
								type: "error",
								shape: shape,
								message: "" + hint + "不可为空"
							});
						}
						if (name.length > 64) {
							me.errors.push({
								type: "error",
								shape: shape,
								message: "" + hint + "长度超过了限制(max:64)"
							})
						}

						$.each(me.chars, function (ch) {
							if (name.indexOf(ch) >= 0) {
								me.errors.push({
									type: "error",
									shape: shape,
									message: "" + hint + "不可包含特殊符号"
								})
							}
						});
						$.each(me.numbers, function (n) {
							if (name.indexOf(n) === 0) {
								me.errors.push({
									type: "error",
									shape: shape,
									message: "" + hint + "不可以数字开头"
								})
							}
						})
					},

					_isSameProcName: function (name, hint, shape) {
						var me = this;

						$.each(me.doc.getAllProcess(), function (p) {
							if (p.id !== shape.id) {
								if (p.name === name) {
									me.errors.push({
										type: "error",
										shape: shape,
										message: "" + hint + "重复的名称"
									});
									found = true;
									return true;
								}
							}
						})
					}
				}
			};

			return wf;
		}
	}

})();